import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TcpEchoSever {
    private ServerSocket serverSocket = null;
    // 注意：服务器本身使用ServerSocket和端口绑定连接
    public TcpEchoSever(int Port) throws IOException {
        serverSocket = new ServerSocket(Port);
    }


    public void start() throws IOException {
        System.out.println("启动服务器!");
        ExecutorService pool = Executors.newCachedThreadPool();

        // 注意：使用while保证每次有客户端连接时都能连接到
        while (true){
            //// 版本一：使用多线程
//            // 注意：每当有一个客户端连接服务器时，创建一个Socket对象和客户端的Socket进行通信
//            Socket clientSocket = serverSocket.accept();
//            // 注意：建立连接使用当前线程，放在我们创建的线程外；使用多线程去处理客户端发来的请求(处理业务)
//            Thread t = new Thread(()->{
//                try {
//                    processConnection(clientSocket);
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            });
//            t.start();

            // 版本二：使用线程池
            Socket clientSocket = serverSocket.accept();
            // 注意：线程池放在while外面创建
            pool.submit(()->{
                try {
                    processConnection(clientSocket);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        }
    }

    // 注意：一个连接对应一个客户端，
    private void processConnection(Socket clientSocket) throws IOException {
        // 注意：服务器的每一个Socket对应一个客户端
        System.out.printf("[%s:%d] 客户端上线!\n",
                clientSocket.getInetAddress().toString(),
                clientSocket.getPort());

        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream()){
            // 注意：由于一个客户端可能要处理多个请求和响应，所以使用循环进行
            while (true){
                // 1.服务器读取客户端写入网卡的字节流数据
                Scanner reqScan = new Scanner(inputStream);
                if (!reqScan.hasNext()){
                    System.out.printf("[%s:%d] 客户端下线!\n",
                            clientSocket.getInetAddress().toString(),
                            clientSocket.getPort());
                    break;
                }
                String request = reqScan.next();
                // 注意：next读到换行符/空格/tab结束，但是读取的内容不包含换行符/空格/tab
                //    我们这里是从客户端的请求内容就读取，所以客户端发来的请求中应当有以上结束符

                // 2.对请求进行业务处理
                String response = process(request);

                // 3.服务器把响应内容写回网卡，响应给客户端
                //   操作：用outputStream构造一个PrintWriter字符流对象，便于把"\n"一并写入网卡
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(response);
                printWriter.flush();   // 冲刷，保证数据写入网卡

                // 4.打印日志
                System.out.printf("[%s:%d] req:%s; resp:%s\n",
                        clientSocket.getInetAddress(),clientSocket.getPort(),request,response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            clientSocket.close();
        }
    }

    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoSever sever = new TcpEchoSever(9090);
        sever.start();
    }
}
